package com.lagou.edu.com.lagou.edu.filter;

import com.alibaba.fastjson.JSON;
import com.lagou.edu.com.lagou.edu.model.IPRecord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.lang.annotation.Annotation;
import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * IP 防爆晒过滤器
 */
@Component
@Slf4j
public class IpAntiExplosionFilter implements GlobalFilter, Order,Runnable {


    private static Map<String, List<IPRecord>> ipRecordMap = new ConcurrentHashMap<>();

    public IpAntiExplosionFilter(){
        // 每隔60s清除最近时间窗口前数据
        Executors.newSingleThreadScheduledExecutor()
                .scheduleWithFixedDelay(new Thread(this)
                        ,60,2, TimeUnit.SECONDS);
    }

    @Value("${ip.maxAccessCount:10}")
    private Integer maxAccessCount;//最近一段时间最大访问次数

    @Value("${ip.accessTimePeriod:1}")
    private Integer accessTimePeriod;//IP限制时间窗口

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 从上下⽂中取出request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 从request对象中获取客户端ip
        String clientIp = request.getRemoteAddress().getHostString();

        //获取访问路径，只拦截注册接口
        URI uri = request.getURI();
        String path = uri.getPath();
        if(!path.contains("/user/register")){
            return chain.filter(exchange);
        }

        //获取IP访问次数，限制请求
        int accessCount = getIpAccessCount(clientIp);
        if(accessCount > maxAccessCount){
            response.setStatusCode(HttpStatus.BAD_GATEWAY); // 状态码
            HttpHeaders httpHeaders = response.getHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            log.info("=====>IP:" + clientIp + " 频繁请求注册接口，请求被拒绝！");
            Map<String, Object> result = new HashMap<>();
            result.put("code", 303);
            result.put("msg", "您频繁进⾏注册，请求已被拒绝");
            String resultStr = JSON.toJSONString(result);
            DataBuffer wrap = response.bufferFactory().wrap(resultStr.getBytes());
            return response.writeWith(Mono.just(wrap));
        }

        // 记录IP访问日志
        List<IPRecord> ipRecords = ipRecordMap.get(clientIp);
        if(CollectionUtils.isEmpty(ipRecords)){
            ipRecords = new ArrayList<>();
        }
        ipRecords.add(new IPRecord(clientIp,1,System.currentTimeMillis()));
        ipRecordMap.put(clientIp,ipRecords);
        return chain.filter(exchange);
    }

    @Override
    public int value() {
        return 0;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return null;
    }

    /**
     * 获取最近一段时间IP访问次数
     * @param ip
     * @return
     */
    private int getIpAccessCount(String ip){
        int ipAccessCount = 0;
        List<IPRecord> ipRecords = ipRecordMap.get(ip);
        if(CollectionUtils.isEmpty(ipRecords)) return 0;

        Long end = System.currentTimeMillis();
        Long start = System.currentTimeMillis() - accessTimePeriod * 1000 * 60;
        for (IPRecord ipRecord : ipRecords) {
            if(ipRecord.getRecordTimestamp() >= start && ipRecord.getRecordTimestamp() <= end){
                ipAccessCount++;
            }
        }
        return ipAccessCount;
    }

    @Override
    public void run() {
        log.info("开始清理冗余ip访问日志");
        Long end = System.currentTimeMillis() - accessTimePeriod * 1000 * 60;
        for (List<IPRecord> ipRecords : ipRecordMap.values()) {
            List<IPRecord> newIpRecords = new ArrayList<>();
            Iterator<IPRecord> ipRecordIterator = ipRecords.iterator();
            while (ipRecordIterator.hasNext()){
                IPRecord next = ipRecordIterator.next();
                if(next.getRecordTimestamp() < end){
                    ipRecordIterator.remove();
                }
            }
        }
    }
}
